var GRID = require('./grid');
var SOCKWRAP = require('./sockwrap');
var LOG = require('./log');

var ROOM_WIDTH = 12;
var ROOM_HEIGHT = 7;
var INIT4POS = [2, 5, 8, 10];
var INIT_GRID_ROW = 3;
var PLAYER_DEF_SPEED = 3;
var PLAYER_DEF_HP = 100;

var STATE_WAITSYNC = 'waitsync';
var STATE_ONSYNC = 'onsync';

var INTURN_TIME = 5 * 1000;
var WAITSYNC_TIME = 10 * 1000;

var TAG_Y = 0;
var TAG_X = 1;

var CONFLICT_HIT = 10;
var GUEST_ID_MAX = 999;

var g_roomid = 1;

exports.TAG_Y = TAG_Y;
exports.TAG_X = TAG_X;
exports.ROOM_WIDTH = ROOM_WIDTH;
exports.ROOM_HEIGHT = ROOM_HEIGHT;


exports.init4 = function (players) {
	var room = new ROOM4();
	room.init(players);
};


function ROOM4() {
    this._grids = new Array(ROOM_HEIGHT);
    for (var h = 0; h < ROOM_HEIGHT; ++h) {
        var row = new Array(ROOM_WIDTH);
        this._grids[h] = row;
    }

    this._players = [];
    this._timer = null;
    this._deadPlayers = [];
    this._renderTime = 0;
    this._evts = [];
    this._id = g_roomid;
    g_roomid += 1;
    LOG.info('create room', [this._id]);
}

ROOM4.prototype.addPlayer = function (player, i) {
    player._id = i+1;
    player_status = STATE_WAITSYNC;
    player._time = new Date();
    player._pos = { y: 0, x: INIT4POS[i] };
    player._speed = PLAYER_DEF_SPEED;
    player._hp = PLAYER_DEF_HP;
    player._maxhp = PLAYER_DEF_HP;
    player._name = 'guest' + Math.ceil(GUEST_ID_MAX * Math.random());

    player._dbuff = {};
    player._kill = 0;
    player._shell = 0;
    player._hide = 0;
    player._radar = 0;
    player._freeze = false;

    if (!player.npc) {
		var thiz = this;
        player.sock.bind('disconnect', function () {
            delete player.sock;
            thiz.removePlayer(player);
        });
        player.sock.bind('quit', function () {
            thiz.removePlayer(player);
        });
    }

    LOG.info('init player', [this._id, player._id, player._name, player._pos.x, player._pos.y]);
    this._players.push(player);
    return player;
}

ROOM4.prototype.init = function (players) {
    players.forEach(function (player, i) {
        this.addPlayer(player, i);
    }, this);

    for (var i = players.length; i < INIT4POS.length; ++i) {
        this.addPlayer({ npc: true }, i);
    }

    this._players.forEach(function (player) {
        if (player.sock) {
            player.sock._sock.emit('start', { h: ROOM_HEIGHT, w: ROOM_WIDTH });
        }
		var packet = { id: player._id, name: player._name, pos: player._pos, speed: player._speed, hp: player._hp };
		this.sendToRoom('playerdata', packet, player);		
    }, this);

	this.sendToRoom('startover');
	
    for (var i = 0; i < INIT_GRID_ROW; ++i) {
        this.generateGridRow();
    }
	
    this.waitSync();
}

ROOM4.prototype.waitSync = function () {
    clearTimeout(this._timer);
    SOCKWRAP.clearEvents(this._evts);
	this.sendToRoom('wait');
	
    this._players.forEach(function (player) {
        if (player.npc) {
            player._status = STATE_ONSYNC;
        }
        else {
            player._status = STATE_WAITSYNC;
        }
    });

	var thiz = this;
    this._players.forEach(function (player) {
        if (player.sock) {
            var evt = player.sock.on('sync', function () {
                thiz.addSync(player);
            });
            this._evts.push(evt);
        }
    }, this);
  
    this._timer = setTimeout(function (room) {
        room.waitTurn();
    }, WAITSYNC_TIME + this._renderTime, this);
}

ROOM4.prototype.autoMove = function (player) {
    if (player._freeze) {
        return;
    }

    function step(speed) {
        var dir = Math.random();
		var rst = Math.ceil(Math.random() * speed);
		if(dir>0.5){
			rst = -1 * rst;
		}
		return rst;
    }
	
	var newX = player._pos.x + step(player._speed);
	newX = clamp(newX, 0, ROOM_WIDTH-1);
	var y1 = clamp(player._pos.y-player._speed, 0, ROOM_HEIGHT-1);
	var y2 = clamp(player._pos.y+player._speed, 0, ROOM_HEIGHT-1);
	for(var y=y1; y<=y2; ++y){
		var newY = clamp(y+1,0, ROOM_HEIGHT-1);
		var grid = this._grids[newY][newX];
		if(grid && grid._type!=GRID.BOMB){
			player._pos = {x:newX, y:y};
			return true;
		}
	}
	return false;
}

ROOM4.prototype.waitTurn = function () {
    clearTimeout(this._timer);
    SOCKWRAP.clearEvents(this._evts);

    this.sendToRoom('time', INTURN_TIME);

    this._players.forEach(function (player) {
        if (player.npc) {
			for(var i=0; i<4; ++i){
				if(this.autoMove(player)){
					break;
				}
			}
        }
    }, this);

    function setPos(player, room) {
        if (player.sock) {
            var evt = player.sock.on('pos', function (pos) {
                room.setPlayerPos(player, pos);
                setPos(player, room);
				player.sock._sock.emit('pos1',pos);
            });
            room._evts.push(evt);
        }
    }
    this._players.forEach(function (player) {
        setPos(player, this);
    }, this);

    this._timer = setTimeout(function (room) {
        room.nextTurn();
    }, INTURN_TIME, this);
}

ROOM4.prototype.generateGridRow = function () {
    for (var h = 1; h < ROOM_HEIGHT; ++h) {
        var curRow = this._grids[h];
        var nextRow = this._grids[h-1];
        for (var w = 0; w < ROOM_WIDTH; ++w) {
            nextRow[w] = curRow[w];
        }
    }

    var genRow = this._grids[ROOM_HEIGHT-1];
    for (var i = 0; i < ROOM_WIDTH; ++i) {
        genRow[i] = GRID.create();
    }

    LOG.info('row', [this._id, genRow.reduce(function (p, n) { return p + ',' + n._type; }, '')]);
	this.sendToRoom('gridrow', genRow.map(function(v){
		if(v._type){
			return v._type;
		}
		else{
			return 0;
		}
	}));
	return this._grids;
}

function clamp(v, rmin, rmax) {
    if (v < rmin) {
        return rmin;
    }
    else if (v > rmax) {
        return rmax;
    }

    return v;
}
exports.clamp = clamp;

ROOM4.prototype.nextTurn = function () {
    clearTimeout(this._timer);
    SOCKWRAP.clearEvents(this._evts);

	this._renderTime = 2000;
    this.generateGridRow();

    this._players.forEach(function (player) {
        var packet = { id: player._id, pos: player._pos };
        LOG.info('set pos', [this._id, player._name, player._speed, player._pos.x, player._pos.y]);
        this.sendToRoom('pos', packet);
    }, this);

    this.bindToGrid();
    this.fixConflict();
    this.executeBuffDebuff();
    for (var z = 0; z < GRID.z_max; ++z) {
        this.executeGrid(z);
    }

    this._players.forEach(function (player) {
        this._grids[player._pos.y][player._pos.x] = null;
    },this);

    this._players = this._players.filter(function (player) {
        return player._hp > 0;
    });

    if (this._players.length == 1) {
        LOG.info('win', [this._id, this._players[0]._name]);
		this.sendToRoom('win', this._players[0]._id);
	}
	else{
	    this.waitSync();
	}
}

ROOM4.prototype.bindToGrid = function () {
    this._players.forEach(function (p) {
        if (p._grid) {
            p._grid._owner = [];
        }
    });

    this._players.forEach(function (player) {
        var y = player._pos.y;
        var x = player._pos.x;
        var grid = this._grids[y][x] = this._grids[y][x] || {};
        if (grid._owner) {
            grid._owner.push(player);
        }
        else {
            grid._owner = [player];
        }
        player._grid = grid;
    }, this);
}

ROOM4.prototype.executeBuffDebuff = function () {
    this._players.forEach(function (player) {
        for (var t in player._dbuff) {
            var dbuff = player._dbuff[t];    
            if (player._hp <= 0) {
                return;
            }

            if (dbuff.update) {
                dbuff.update(player, this);
            }
        }
    }, this);
}

ROOM4.prototype.executeGrid = function (z) {
    this._players.forEach(function (player) {
        var grid = player._grid;
        if (grid && grid._z == z) {
            delete player._grid;
            if (grid.enter) {
				this.sendToRoom('pick', {id:player._id, t:grid._type});
                grid.enter(player, this);
            }
        }      
    }, this);
}

ROOM4.prototype.onPlayerHit = function (shooter, hitter, hit) {
    var room = this;
    function buffOnHit(hitter) {
        for (var t in hitter._dbuff) {
            var dbuff = hitter._dbuff[t];
            if (dbuff && dbuff.onhit) {
                dbuff.onhit(hitter, room);
            }
        }
    }

    if (hitter._shell == 0 && hit>0) {
        hit = (shooter._power && shooter._power * hit) || hit;
        hitter._hp -= hit;
        buffOnHit(hitter);
    }
    if (0 == hit) {
        buffOnHit(hitter);
    }

    LOG.info('hit', [this._id, shooter._name, hitter._name, hit, hitter._hp]);
    if (hitter._hp <= 0) {
        hitter._order = this._players.length;
        shooter._kill += 1;
        this._deadPlayers.push(hitter);
    }
}

ROOM4.prototype.fixConflict = function () {
    function resetPlayerPos(winner, player, room) {
        var modify = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]];

        player._grid = null;
        for (var i = 0; i < modify.length; ++i) {
            var step = Math.floor(Math.random() * modify.length);
            step = modify[step];
            var y = step[TAG_Y] + winner._pos.y;
            y = clamp(y, 0, ROOM_HEIGHT - 1);
            var x = step[TAG_X] + winner._pos.x;
            x = clamp(x, 0, ROOM_WIDTH - 1);
            var grid = room._grids[y][x] = room._grids[y][x] || {};
            if (!grid._owner || grid._owner.length==0) {
                grid._owner = [player];
                player._grid = grid;
                player._pos = { y: y, x: x };
                LOG.info('conflict', [room._id, player._name, x, y]);
                player._hp -= CONFLICT_HIT;
                room.onPlayerHit(winner, player, 0);
                room.sendToRoom('conflictfail', {a:winner._id, b: player._id, pos: player._pos, hp: player._hp });
                return;
            }
        }

        player._hp = -100;
        room.onPlayerHit(winner, player, 0);
        room.sendToRoom('conflictfail', { a:winner._id, b: player._id, hp:0 });
    }

    this._players.forEach(function (player, i) {
        if (!player._grid) {
            return;
        }

        var owners = player._grid._owner;
        if (owners.length > 1) {
            var dice = owners.map(function () { return Math.random(); });
            var winner = dice.indexOf(Math.max.apply(null, dice));
            winner = owners[winner];

            this.sendToRoom('conflict', {pos:winner._pos, id:owners.map(function (p) { return p._id; })});
            owners.forEach(function (cp) {
                if (cp != winner) {
                    resetPlayerPos(winner, cp, this);
                }
            }, this);
            winner._grid._owner = [winner];
            this._renderTime += 2000;
        }
    }, this);
}

ROOM4.prototype.setPlayerPos = function (player, pos) {
    if (player._freeze) {
        return;
    }

    var ystep = Math.abs(player._pos.y - pos.y);
    var xstep = Math.abs(player._pos.x - pos.x);
    if (xstep > player._speed || ystep > player._speed) {
        return;
    }

    var y = clamp(pos.y, 0, ROOM_HEIGHT - 1);
    var x = clamp(pos.x, 0, ROOM_WIDTH - 1);
    if (y != pos.y || x != pos.x) {
        return;
    }

    player._pos = pos;
    return player;
}

ROOM4.prototype.removePlayer = function (player) {
    if (!player._order) {
        player._order = this._players.length;
    }
    if (player.sock) {
        player.sock._sock.emit('quit', { order: player._order, kill: player._kill });
    }

    LOG.info('quit', [this._id, player._name]);
    this.sendToRoom('quit', { id: player._id });
    var i = this._players.indexOf(player);
    delete this._players[i];
}

ROOM4.prototype.addSync = function (player) {
    player._status = STATE_ONSYNC;
    this.sendToRoom('sync', player._id, null);
    LOG.info('sync', [this._id, player._name]);

    var cnt = 0;
    this._players.forEach(function (player) {
        if (player._status == STATE_ONSYNC) {
            cnt += 1;
        }
    });

    if (cnt == this._players.length) {
        this.waitTurn();
    }
}

ROOM4.prototype.sendToRoom = function (cmd, packet, selfo) {
    this._players.forEach(function (player) {
        if (player.sock && player._hp>0) {
            if (player == selfo) {
                packet.self = true;
            }
            player.sock._sock.emit(cmd, packet);
			if(packet){
				delete packet.self;
			}
        }
    });

    this._deadPlayers.forEach(function (player) {
        if (player.sock) {
            player.sock._sock.emit(cmd, packet);
        }
    });
}

